QT从入门到入土(五(1)) 您所在的位置:网站首页 join 多线程 QT从入门到入土(五(1))

QT从入门到入土(五(1))

2024-06-18 19:56| 来源: 网络整理| 查看: 265

引言

前面几篇已经对C++的线程做了简单的总结,浅谈C++11中的多线程(三) - 唯有自己强大 - 博客园 (cnblogs.com)。本篇着重于Qt多线程的总结与实现。

跟C++11中很像的是,Qt中使用QThread来管理线程,一个QThread对象管理一个线程,在使用上有很多跟C++11中相似的地方,但更多的是Qt中独有的内容。另外,QThread对象也有消息循环exec()函数,即每个线程都有一个消息循环,用来处理自己这个线程的事件。

一,知识回顾

首先先来回顾一下一些知识点:

1,为什么需要多线程?

解决耗时操作堵塞整个程序的问题,一般我们会将耗时的操作放入子线程中

2,进程和线程的区别:

进程:一个独立的程序,拥有独立的虚拟地址空间,要和其他进程通信,需要使用进程通信的机制。

线程:没有自己的资源,都是共享进程的虚拟地址空间,多个线程通信存在隐患。

ps:在操作系统每一个进程都拥有独立的内存空间,线程的开销远小于进程,一个进程可以拥有多个线程。(因此我们常用多线程并发,而非多进程并发)

为了更容易理解多线程的作用,先看一个实例:

在主线程中运行一个10s耗时的操作。(通过按钮来触发)

#include "threadtest.h" #include"qthread.h" Threadtest::Threadtest(QWidget* parent) : QMainWindow(parent) { ui.setupUi(this); connect(ui.btn_start, &QPushButton::clicked, this, &Threadtest::on_pushButton_clicked); } void Threadtest::on_pushButton_clicked() { QThread::sleep(10);//主线程 }

可以看到程序运行过程中,整个线程都在响应10秒的耗时操作,对于线程的消息循环exec()函数就未响应了(就是你在这个过程中拖动界面是无反应的)

二,线程类 QThread

Qt 中提供了一个线程类,通过这个类就可以创建子线程了,Qt 中一共提供了两种创建子线程的方式,后边会依次介绍其使用方式。先来看一下这个类中提供的一些常用 API 函数:

2.1 常用共用成员函数 // QThread 类常用 API // 构造函数 QThread::QThread(QObject *parent = Q_NULLPTR); // 判断线程中的任务是不是处理完毕了 bool QThread::isFinished() const; // 判断子线程是不是在执行任务 bool QThread::isRunning() const; // Qt中的线程可以设置优先级 // 得到当前线程的优先级 Priority QThread::priority() const; void QThread::setPriority(Priority priority); 优先级: QThread::IdlePriority --> 最低的优先级 QThread::LowestPriority QThread::LowPriority QThread::NormalPriority QThread::HighPriority QThread::HighestPriority QThread::TimeCriticalPriority QThread::InheritPriority --> 最高的优先级, 默认是这个 // 退出线程, 停止底层的事件循环 // 退出线程的工作函数 void QThread::exit(int returnCode = 0); // 调用线程退出函数之后, 线程不会马上退出因为当前任务有可能还没有完成, 调回用这个函数是 // 等待任务完成, 然后退出线程, 一般情况下会在 exit() 后边调用这个函数 bool QThread::wait(unsigned long time = ULONG_MAX); 2.2 信号槽 // 和调用 exit() 效果是一样的 // 代用这个函数之后, 再调用 wait() 函数 [slot] void QThread::quit(); // 启动子线程 [slot] void QThread::start(Priority priority = InheritPriority); // 线程退出, 可能是会马上终止线程, 一般情况下不使用这个函数 [slot] void QThread::terminate(); // 线程中执行的任务完成了, 发出该信号 // 任务函数中的处理逻辑执行完毕了 [signal] void QThread::finished(); // 开始工作之前发出这个信号, 一般不使用 [signal] void QThread::started(); 2.3静态函数 // 返回一个指向管理当前执行线程的QThread的指针 [static] QThread *QThread::currentThread(); // 返回可以在系统上运行的理想线程数 == 和当前电脑的 CPU 核心数相同 [static] int QThread::idealThreadCount(); // 线程休眠函数 [static] void QThread::msleep(unsigned long msecs); // 单位: 毫秒 [static] void QThread::sleep(unsigned long secs); // 单位: 秒 [static] void QThread::usleep(unsigned long usecs); // 单位: 微秒  三,Qt中实现多线程的两种方法

🧡🧡3.1.派生QThread类对象的方法(重写Run函数)

首先,以文字形式来说明需要哪几个步骤。

自定义一个自己的类,使其继承自QThread类; 在自定义类中覆写QThread类中的虚函数run()。

这很可能就是C++中多态的使用。补充一点:QThread类继承自QObject类。

这里要重点说一下run()函数了。它作为线程的入口,也就是线程从run()开始执行,我们打算在线程中完成的工作都要写在run()函数中,个人认为可以把run()函数理解为线程函数。这也就是子类覆写基类的虚函数,基类QThread的run()函数只是简单启动exec()消息循环,关于这个exec()后面有很多东西要讲,请做好准备。那么我们就来尝试用多线程实现10s耗时的操作:(用按钮触发)

1️⃣在编辑好ui界面后,先创建一个workThread的类。(继承自QThread类)

2️⃣在workThread1的类中重写run函数

在workThread.h的声明run函数:

#include class workThread : public QThread { public: void run(); };

在workThread.cpp中重写run函数(并打印子线程的ID):

#include "workThread.h" #include"qdebug.h" workThread::workThread(QObject* parent) { } //重写run函数 void workThread::run() { qDebug() moveToThread(qthread1); //线程结束时清理线程内存 connect(qthread1, &QThread::finished, qthread1, &QThread::deleteLater); //将按钮事件(信号)绑定槽函数 connect(ui.btn_start, &QPushButton::clicked, thread, &workThread::dowork); //打印主线程 connect(ui.btn_start, &QPushButton::clicked, [=]() { qDebug() start(); }); connect(thread, &myThread::sendArray, bub_thread, &BubbleSort::rcvArray); //3接收子线程发送的数据 connect(thread, &myThread::sendArray, [=](QVectorlist) { for (int i = 0; i < list.size(); ++i) { ui.randlist->addItem(QString::number(list.at(i))); } }); connect(thread, &myThread::sendArray, [=](QVectorlist){ bub_thread->start(); }); connect(bub_thread, &BubbleSort::finish, [=](QVectorlist) { for (int i = 0; i < list. size(); ++i) { ui.bubblelist->addItem(QString::number(list.at(i))); } }); }

实现效果:

方法二:moveToThread() 

思路:

新建myThread类,用于生成随机数(working函数),在接受到主线程的信号后开始生成随机数 新建BubbleSort类,用于排序(working函数),在接受到myThread类生成的随机数后开始排序 最后显示在界面

代码实现:

子线程.h #pragma once #include"qthread.h" #include"qvector.h" #include"qobject.h" //新建随机数类 class myThread : public QObject { Q_OBJECT public: myThread(QObject* parent = nullptr); void working(int num);//生成随机数 signals: void sendArray(QVectornum);//发送 private: int m_num; }; //新建冒泡排序类 class BubbleSort : public QObject { Q_OBJECT public: BubbleSort(QObject* parent = nullptr); void working(QVectorlist);//要接收的是排序的数 signals: void finish(QVectorlist);//排序完成后发送一个finish信号 }; 子线程.cpp #include "myThread.h" #include"qelapsedtimer.h" #include"qdebug.h" myThread::myThread(QObject* parent) :QObject(parent) { } BubbleSort::BubbleSort(QObject* parent) : QObject(parent) { } void myThread::working(int num) { qDebug() moveToThread(thread1); bub->moveToThread(thread2); //4启动子线程 //先向子线程发送要生成的随机数个数 connect(this,&list::stating, myth, &myThread::working); //再启动子线程 connect(ui.pushButton, &QPushButton::clicked, [=]() { emit stating(1000);//主线程设置子线程随机数的个数 thread1->start(); }); //将生成好的随机数发送给BubbleSort类 connect(myth, &myThread::sendArray, bub, &BubbleSort::working); //将生成好的随机数显示在界面 connect(myth, &myThread::sendArray, [=](QVectorlist) { for (int i = 0; i < list.size(); ++i) { ui.randlist->addItem(QString::number(list.at(i))); } }); //发送的同时启动排序算法 connect(myth, &myThread::sendArray, [=](QVectorlist){ thread2->start(); }); //将排序好的数显示在界面 connect(bub, &BubbleSort::finish, [=](QVectorlist) { for (int i = 0; i < list. size(); ++i) { ui.bubblelist->addItem(QString::number(list.at(i))); } }); }

结论

通过对比,我们可以发现:

由于第二种方法,我们可以自定义带参的子线程运行函数,因此代码更加简洁。 在第二种方法中,我们还可以随意修改需要在哪个线程中运行,代码也更加灵活。 第一种方法适合在线程中处理单一事件,其逻辑简单(只需要新建一个继承自QThread类的对象,重写run函数,然后启动即可),对于需要在一个线程中处理多个事件,还是用第二种方法比较好。 为什么不能在第二种方法中,给定义的子线程对象添加父类呢? :由于添加了父类以后就不能再移动到QThread中去了

如何进行线程资源的释放?

在new对象时候,直接用this指定其父类(即放入对象数中) 在程序最后自行释放资源 connect(this, &list::destroyed, this, [=]() { thread1->quit(); thread1->wait(); thread1->deleteLater(); thread2->quit(); thread2->wait(); thread2->deleteLater(); myth->deleteLater(); bub->deleteLater(); });


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有